package ouhenry.stage.gateway.component;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

import springfox.documentation.swagger.web.InMemorySwaggerResourcesProvider;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;

/**
 * 官方文档写法，聚合各个服务的swagger接口，实现在网关统一管理文档，拼接项目路由：
 * {@link springfox.documentation.swagger.web.InMemorySwaggerResourcesProvider#get()}
 * 
 * @see springfox.documentation.swagger.web.ApiResourceController#swaggerResources()
 *      swaggerResources方法，汇总各个服务的地址。由于@Primary注解，它将会调用这个get方法。
 * 
 * @version 1.0
 */
@Configuration
@Component
public class SwaggerWsEndpointsConfig {

    private static final Logger logger = LoggerFactory.getLogger(SwaggerWsEndpointsConfig.class);

    @Value("${spring.application.name}")
    private String self;

    private final RouteLocator routeLocator;

    @Autowired
    public SwaggerWsEndpointsConfig(RouteLocator routeLocator) {
        this.routeLocator = routeLocator;
        logger.error("路由初始化：{}", routeLocator.getRoutes().filter(route -> route != null));
    }

    /**
     * 注解@Primary是必须的，指明默认配置，否则ApiResourceController将不执行这个方法。官方文档有详细说明这个问题。
     * 
     * @return
     */
    @Primary
    @Bean
    public SwaggerResourcesProvider swaggerResourcesProvider(
            InMemorySwaggerResourcesProvider defaultResourcesProvider) {
        return () -> {
            List<String> routeHosts = new ArrayList<>();
            // 获取所有可用的host：serviceId，如果网关不提供接口，可以排除本身
            routeLocator.getRoutes().filter(route -> route.getUri().getHost() != null)
                    .filter(route -> !self.equals(route.getUri().getHost()))
                    .subscribe(route -> routeHosts.add(route.getUri().getHost()));
            logger.error("当前路由数量:{}", routeHosts.size());
            List<SwaggerResource> resources = new ArrayList<>(defaultResourcesProvider.get());

            Set<String> dealed = new HashSet<>();
            // 记录已经添加过的server，存在同一个应用注册了多个服务在nacos上
            for (String route : routeHosts) {
                if (!dealed.contains(route)) {
                    logger.error("当前路由数量:{}", route);
                    SwaggerResource wsResource = new SwaggerResource();
                    wsResource.setName(route);
                    // 默认会在地址前加入ip+端口,但是没有项目名称,v2则不存在这个问题。v3存在这个问题没有解决，不知是不是3.0的bug。
                    wsResource.setLocation("/" + route + "/v2/api-docs");
                    wsResource.setSwaggerVersion("2.0");
                    resources.add(wsResource);
                }
            }
            logger.error("当前路由数量:{}", routeHosts.size());
            return resources;
        };

    }
}